// service/log_service/runtime_log.go
// =============================================================================
// | 包: log_service                                                         |
// | 功能: 运行时日志服务实现                                                |
// | 说明: 提供运行时日志记录功能，包括日志项的添加和保存到数据库             |
// =============================================================================
package log_service

import (
	"blog_server/global"      // 全局配置包
	"blog_server/models"      // 模型包
	"blog_server/models/enum" // 枚举包
	"encoding/json"           // JSON编码包
	"fmt"                     // 格式化包
	"reflect"                 // 反射包
	"strings"                 // 字符串处理包
	"time"                    // 时间包

	"github.com/pkg/errors"      // 错误处理包
	"github.com/sirupsen/logrus" // 日志记录包
)

// =============================================================================
// | 结构体: RuntimeLog                                                      |
// | 功能: 运行时日志记录结构体                                              |
// =============================================================================
type RuntimeLog struct {
	level           enum.LogLevelType // 日志级别
	title           string            // 日志标题
	itemList        []string          // 日志项列表
	serviceName     string            // 服务名称
	runtimeDateType RuntimeDateType   // 时间分割类型
}

// =============================================================================
// | 方法: SetTitle                                                          |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @title - string (日志标题)                                         |
// | 功能: 设置日志标题                                                      |
// =============================================================================
func (ac *RuntimeLog) SetTitle(title string) {
	ac.title = title // 设置标题
}

// =============================================================================
// | 方法: SetLink                                                           |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (链接标签)                                         |
// |       @href - string (链接地址)                                          |
// | 功能: 添加链接类型的日志项                                              |
// =============================================================================
func (ac *RuntimeLog) SetLink(label string, href string) {
	ac.itemList = append(ac.itemList, fmt.Sprintf(
		`<div class="log_item link">
            <div class="log_item_label">%s</div>
            <a href="%s" target="_blank">%s</a>
        </div>`,
		label, href, href,
	))
}

// =============================================================================
// | 方法: SetImage                                                          |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @src - string (图片地址)                                           |
// | 功能: 添加图片类型的日志项                                              |
// =============================================================================
func (ac *RuntimeLog) SetImage(src string) {
	ac.itemList = append(ac.itemList, fmt.Sprintf("%s|", src, "%s|", src))
}

// =============================================================================
// | 方法: setItem                                                           |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (日志项标签)                                       |
// |       @value - any (日志项值)                                            |
// |       @logLevelType - enum.LogLevelType (日志级别)                       |
// | 功能: 添加通用的日志项                                                  |
// |----------------------------------------------------------------------------|
// | 流程:                                                                   |
// | 1. 根据值类型判断是否需要JSON序列化                                     |
// | 2. 将值转换为字符串                                                     |
// | 3. 构造HTML格式的日志项并添加到列表                                     |
// =============================================================================
func (ac *RuntimeLog) setItem(label string, value any, logLevelType enum.LogLevelType) {

	var v string
	t := reflect.TypeOf(value) // 获取值类型

	// 根据值类型进行转换处理
	switch t.Kind() {
	case reflect.Struct, reflect.Map, reflect.Slice: // 复杂类型处理
		byteData, _ := json.Marshal(value) // 序列化复杂类型
		v = string(byteData)               // 转换为字符串
	default: // 简单类型处理
		v = fmt.Sprintf("%v", value) // 格式化为字符串
	}

	ac.itemList = append(ac.itemList,
		fmt.Sprintf("<div class=\"%s\"><div class=\"log_item_label\">%s</div>%s<div>",
			logLevelType, label, v))
}

// =============================================================================
// | 方法: SetItem                                                           |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (日志项标签)                                       |
// |       @value - any (日志项值)                                            |
// | 功能: 添加INFO级别的日志项                                              |
// =============================================================================
func (ac *RuntimeLog) SetItem(label string, value any) {
	ac.setItem(label, value, enum.LogInfoLevel)
}

// =============================================================================
// | 方法: SetItemInfo                                                       |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (日志项标签)                                       |
// |       @value - any (日志项值)                                            |
// | 功能: 添加INFO级别的日志项                                              |
// =============================================================================
func (ac *RuntimeLog) SetItemInfo(label string, value any) {
	ac.setItem(label, value, enum.LogInfoLevel)
}

// =============================================================================
// | 方法: SetItemWarn                                                       |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (日志项标签)                                       |
// |       @value - any (日志项值)                                            |
// | 功能: 添加WARN级别的日志项                                              |
// =============================================================================
func (ac *RuntimeLog) SetItemWarn(label string, value any) {
	ac.setItem(label, value, enum.LogWarnLevel)
}

// =============================================================================
// | 方法: SetItemError                                                      |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (日志项标签)                                       |
// |       @value - any (日志项值)                                            |
// | 功能: 添加ERROR级别的日志项                                             |
// =============================================================================
func (ac *RuntimeLog) SetItemError(label string, value any) {
	ac.setItem(label, value, enum.LogErrLevel)
}

// =============================================================================
// | 方法: SetNowTime                                                        |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 功能: 记录当前时间到日志项中                                            |
// =============================================================================
func (ac *RuntimeLog) SetNowTime() {
	// 添加格式化时间
	ac.itemList = append(ac.itemList, fmt.Sprintf(
		`<div class="log_time">%s</div>`,
		time.Now().Format("2006-01-02 15:04:05"),
	))
}

// =============================================================================
// | 方法: SetError                                                          |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 参数: @label - string (错误标签)                                         |
// |       @err - error (错误对象)                                            |
// | 功能: 记录错误信息到日志项中                                            |
// |----------------------------------------------------------------------------|
// | 流程:                                                                   |
// | 1. 为错误添加调用栈信息                                                 |
// | 2. 使用logrus记录错误                                                   |
// | 3. 构造HTML格式的错误信息并添加到日志项列表                             |
// =============================================================================
func (ac *RuntimeLog) SetError(label string, err error) {
	// fmt.Println(label, err)
	msg := errors.WithStack(err) // 添加调用栈
	// fmt.Printf("%+v\n", msg)
	logrus.Errorf("%s %s", label, err.Error()) // 记录错误日志

	ac.itemList = append(ac.itemList, fmt.Sprintf(
		`<div class="log">%s: %s %T \n%+v</div>`,
		label, err, err, msg,
	))
}

// =============================================================================
// | 方法: Save                                                              |
// | 接收者: @ac - *RuntimeLog (运行时日志实例)                               |
// | 功能: 保存运行时日志到数据库                                            |
// |----------------------------------------------------------------------------|
// | 流程:                                                                   |
// | 1. 添加当前时间戳                                                       |
// | 2. 根据serviceName和logType查询是否存在相同的服务日志记录               |
// | 3. 如果存在则更新内容，否则创建新的日志记录                             |
// =============================================================================
func (ac *RuntimeLog) Save() {

	ac.SetNowTime()

	// 声明日志模型变量
	var log models.LogModel

	// 执行数据库查询
	global.DB.Where(fmt.Sprintf("service_name = ? and log_type = ? and created_at >= date_sub(now(), %s)", ac.runtimeDateType.GetSqlTime()),
		ac.serviceName, enum.RuntimeLogType).Find(&log)

	// 处理日志内容
	content := strings.Join(ac.itemList, "\n")

	if log.ID != 0 { // 检查记录是否存在

		// 更新操作
		c := strings.Join(ac.itemList, "\n")
		newContent := log.Content + "\n" + c

		// 执行更新
		global.DB.Model(&log).Updates(map[string]any{
			"content": newContent,
		})
		ac.itemList = []string{}

		return // 返回
	}

	// 执行数据库插入
	err := global.DB.Create(&models.LogModel{
		LogType:     enum.RuntimeLogType, // 日志类型
		Title:       ac.title,            // 日志标题
		Content:     content,             // 日志内容
		Level:       ac.level,            // 日志级别
		ServiceName: ac.serviceName,      // 服务名称
	}).Error

	if err != nil {
		logrus.Errorf("创建运行日志错误 %s", err)
		return
	}
	ac.itemList = []string{}
}

// =============================================================================
// | 类型: RuntimeDateType                                                   |
// | 功能: 定义运行时日志的时间分割类型                                      |
// =============================================================================
type RuntimeDateType int8

// =============================================================================
// | 常量: RuntimeDateType                                                   |
// | 功能: 定义运行时日志的不同时间分割粒度                                  |
// =============================================================================
const (
	RuntimeDateHour  RuntimeDateType = 1 // 按小时分割
	RuntimeDateDay   RuntimeDateType = 2 // 按天分割
	RuntimeDateWeek  RuntimeDateType = 3 // 按周分割
	RuntimeDateMonth RuntimeDateType = 4 // 按月分割
)

// =============================================================================
// | 方法: GetSqlTime                                                        |
// | 接收者: @r - RuntimeDateType (时间分割类型)                              |
// | 返回: @string - SQL时间间隔表达式                                        |
// | 功能: 返回对应时间类型的SQL间隔表达式                                   |
// |----------------------------------------------------------------------------|
// | 示例:                                                                   |
// | -- 查询过去1小时内的日志记录                                             |
// | select * from log_models where created_at >= date_sub(now(), interval 1 HOUR)
// | -- 查询过去1天内的日志记录                                               |
// | select * from log_models where created_at >= date_sub(now(), interval 1 DAY)
// | -- 查询过去1周内的日志记录                                               |
// | select * from log_models where created_at >= date_sub(now(), interval 1 WEEK)
// | -- 查询过去1月内的日志记录                                               |
// | select * from log_models where created_at >= date_sub(now(), interval 1 MONTH)
// =============================================================================
func (r RuntimeDateType) GetSqlTime() string {
	switch r {
	case RuntimeDateHour:
		return "interval 1 HOUR" // 小时粒度SQL表达式
	case RuntimeDateDay:
		return "interval 1 DAY" // 天粒度SQL表达式
	case RuntimeDateWeek:
		return "interval 1 WEEK" // 周粒度SQL表达式
	case RuntimeDateMonth:
		return "interval 1 MONTH" // 月粒度SQL表达式
	}
	return "interval 1 DAY" // 默认返回天粒度
}

// =============================================================================
// | 函数: NewRuntimeLog                                                     |
// | 参数: @serviceName - string (服务名称)                                   |
// |       @dateType - RuntimeDateType (时间分割类型)                         |
// | 返回: @*RuntimeLog - 运行时日志实例                                      |
// | 功能: 创建并返回RuntimeLog实例的工厂函数                                |
// =============================================================================
func NewRuntimeLog(serviceName string, dateType RuntimeDateType) *RuntimeLog {
	return &RuntimeLog{
		serviceName:     serviceName,
		runtimeDateType: dateType,
	}
}
